//-----------------------------------------------------------------------------
//
// CodeXS - Copyright(c) Businessware Architects cc
// Contact: Willem J Fourie
// E-mail : willemf@bware.biz
//
// This file is part of CodeXS.
//
// CodeXS is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License (GPL) as 
// published by the Free Software Foundation version 2 of the License.
//
// CodeXS is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with CodeXS; if not, write to:
//
// Free Software Foundation, Inc.
// 59 Temple Place
// Suite 330
// Boston
// MA  02111-1307  USA
//
//-----------------------------------------------------------------------------

using System;
using System.Text;
using System.Collections;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Net;
using Microsoft.CSharp;
using Microsoft.VisualBasic;

//#if NET_2_0
using System.Xml.Serialization.Advanced;
//#endif // NET_2_0

namespace CodeXS.Schema
{
	/// <summary>
	/// ICodeModifier implementors modify the <see cref="CodeDOM"/> 
	/// produced by the standard XSD code generator.
	/// </summary>
	public interface ICodeModifier
	{
		/// <summary>
		/// Returns the name of the code modifier.
		/// </summary>
		/// <remarks>
		/// Used by the <see cref="CodeGenerator"/> to identify
		/// the code modifier in the generated code header.
		/// </remarks>
		string Name { get; }
		/// <summary>
		/// Returns the version number of the code modifier.
		/// </summary>
		/// <remarks>
		/// Used by the <see cref="CodeGenerator"/> to identify
		/// the code modifier in the generated code header.
		/// </remarks>
		string Version { get; }
		/// <summary>
		/// This implemented method modifies the XSD <see cref="CodeDOM"/>.
		/// </summary>
		/// <param name="CodeGenerator">The code generator object that hosts
		/// the <see cref="CodeNamespace"/> objects for the schema(s).
		/// </param>
		void Execute(CodeGenerator CodeGenerator);
		/// <summary>
		/// Enabled flag. True if enabled, else false if disabled.
		/// </summary>
		/// <remarks>
		/// To include this code modifier in the list of executable code
		/// modifiers, set the property to return true, else if to be
		/// excluded, return false.
		/// </remarks>
		bool Enabled { get; }
		/// <summary>
		/// Run once flag. True if code modifier is to be executed once only,
		/// else false.
		/// </summary>
		/// <remarks>
		/// To execute this code modifier for every <see cref="CodeDOM"/>,
		/// return false. If the code modifier is to be executed only once,
		/// return true.
		/// </remarks>
		bool RunOnce { get; }
		/// <summary>
		/// Initializes the interface with the reference to the
		/// <see cref="CodeGenerator"/>.
		/// </summary>
		/// <remarks>
		/// Allows the interface implementation to initialize
		/// itself.
		/// </remarks>
		/// <param name="CodeGenerator">The instance of the 
		/// CodeGenerator which will call the interface when 
		/// required via the <see cref="Execute"/> method.
		/// </param>
		void Initialize(CodeGenerator CodeGenerator);
		/// <summary>
		/// De-initializes the interface with the reference to the
		/// <see cref="CodeGenerator"/>.
		/// </summary>
		/// <remarks>
		/// Allows the interface implementation to de-initialize
		/// itself.
		/// </remarks>
		/// <param name="CodeGenerator">The instance of the 
		/// CodeGenerator which will call the interface when 
		/// required via the <see cref="Execute"/> method.
		/// </param>
		void Deinitialize(CodeGenerator CodeGenerator);
		/// <summary>
		/// Returns a <see cref="SchemaImporterExtension"/> object.
		/// </summary>
		/// <remarks><para>
		/// This interface method allows the code modifier to
		/// participate in generating schema-to-code mappings.
		/// </para>
		/// <para>
		/// Only available in .NET 2.0 or newer.
		/// </para></remarks>
		object SchemaImporterExtension { get; }
	}
	// CHANGE: 2006-06-26:	  Version 0.57 Beta
	// Removed ISchemaObjectReader interface.
	// Reasons:
	// 1. The ICodeModifer interface provides the correct entry points for
	//    enhancements requiring direct reading from the XML schemas.
	// 2. The ICodeModifer interface supports the .NET 2.0 
	//    SchemaImporterExtension class implementation in the 
	//    ICodeModifier interface.
	// 3. The ICodeModifer interface supports initialization, which
	//    can be used as an entry point to pre-read schemas.
	public interface IReplaceParameter
	{
		string Replace(CodeGenerator CodeGenerator, string Name, object Value);
		bool Enabled { get; }
	}
	// Checks for possible generation problems...
	public class CodeGenerateCheck
	{
		static public void Execute(CodeGenerator CodeGenerator)
		{
			CodeTypeDeclarationCollection types = new CodeTypeDeclarationCollection();
			foreach (string key in CodeGenerator.CodeNamespaces.Keys)
			{
				CodeNamespace ns = CodeGenerator.CodeNamespaces[key] as CodeNamespace;
				foreach (CodeTypeDeclaration type in ns.Types)
				{
					// CHANGE: 2006-06-19:	  Version 0.56 Beta
					// Test for duplicate type names no longer required.
					// Tested and fixed in method 'ExtractNamespace'.
					types.Add(type);
					switch (type.Name)
					{
						case "string":
							CodeGenerator.StringTypeDefined = true;
							break;
						case "date":
							CodeGenerator.DateTypeDefined = true;
							break;
					}
				}
			}
			types.Clear();
		}
	}
	public class CodeGenerator
	{
		private ArrayList _warnings = new ArrayList();
		public string Version
		{
			get { return "0.57 Beta - 13 Jul 2006"; }
		}
		public string[] Warnings
		{
			get { return (string[])_warnings.ToArray(typeof(string)); }
		}
		public void AddWarning(string Warning)
		{
			Warning = Warning.Trim();
			Warning = Warning.Trim(new char[]{'\r','\n'});
			Warning = Warning.Trim();
			if(Warning.Substring(0, 7).ToLower() == "warning")
			{
				Warning = Warning.Substring(7);
			}
			Warning = Warning.TrimStart();
			Warning = Warning.TrimStart(new char[]{':','.',';'});
			Warning = Warning.TrimStart();
			Warning = "Warning: " + Warning;
			foreach(string s in _warnings)
			{
				if(Warning == s) return;
			}
			_warnings.Add(Warning);
		}
		public void ClearWarnings()
		{
			_warnings.Clear();
		}
		private CodeNamespace _globalCodeNamespace = null;
		public CodeNamespace GlobalCodeNamespace
		{
			get { return _globalCodeNamespace; }
		}
		private CodeNamespace _commonCodeNamespace = null;
		public CodeNamespace CommonCodeNamespace
		{
			get { return _commonCodeNamespace; }
		}
		private Hashtable _sourceCodes = new Hashtable();
		public string[] SourceCodes
		{
			get
			{
				string[] codes = new string[_sourceCodes.Keys.Count];
				int i = 0;
				foreach(string key in _sourceCodes.Keys)
				{
					string code = _sourceCodes[key] as string;
					string filePath = _codeFilePaths[key] as string;
					codes[i++] = filePath + "\n" + code;
				}
				return codes;
			}
		}
		public int NumberOfCodeFiles
		{
			get { return _sourceCodes.Count; }
		}
		public string GetSourceCode(string UriPath)
		{
			string source = _sourceCodes[UriPath] as string;
			if(source == null) source = "";
			return source;
		}
		public void SetSourceCode(string UriPath, string Source)
		{
			_sourceCodes[UriPath] = Source;
		}
		private Hashtable _replaceParms = new Hashtable();
		public object ReplaceableParameter(string Param)
		{
			return _replaceParms[Param];
		}
		public void ReplaceableParameter(string Param, object Value)
		{
			if (Value == null) _replaceParms.Remove(Param);
			else _replaceParms[Param] = Value;
		}
		static private string _codeModifierInterface = "ICodeModifier";
		private string _codeFileFolder = "";
		public string CodeFileFolder
		{
			get { return _codeFileFolder; }
			set
			{
				_codeFileFolder = value;
				if(_codeFileFolder == null) _codeFileFolder = "";
				else
				{
					if( ! _codeFileFolder.EndsWith(@"\"))
					{
						_codeFileFolder += @"\";
					}
				}
			}
		}
		private string[] _codeModifierAssemblyPaths = null;
		private string[] CodeModifierAssemblyPaths
		{
			get { return _codeModifierAssemblyPaths; }
			set { _codeModifierAssemblyPaths = value; }
		}
		private ICodeModifier[] _codeModifiers = null;
		public ICodeModifier[] CodeModifiers
		{
			get { return _codeModifiers; }
			set { _codeModifiers = value; }
		}
		// CHANGE: 2006-06-26:	  Version 0.57 Beta
		// Code modifiers can now be SchemaImporterExtension sub-classes.
//#if NET_2_0
		private SchemaImporterExtension[] _schemaImporterExtenders = null;
		private SchemaImporterExtension[] SchemaImporterExtenders
		{
			get { return _schemaImporterExtenders; }
			set { _schemaImporterExtenders = value; }
		}
//#endif // NET_2_0
		static private CodeGeneratorOptions _codeGeneratorOptions = null;
		static public CodeGeneratorOptions CodeGeneratorOptions
		{
			get { return _codeGeneratorOptions; }
			set { _codeGeneratorOptions = value; }
		}
		private Hashtable _codeFilePaths = new Hashtable();
		public string[] CodeFilePaths
		{
			get
			{
				string[] paths = new string[_codeFilePaths.Keys.Count];
				int i = 0;
				foreach(string key in _codeFilePaths.Keys)
				{
					string path = _codeFilePaths[key] as string;
					paths[i++] = path;
				}
				return paths;
			}
		}
		public string GetSchemaNamespace(string CodeFilePath)
		{
			string ns = null;
			string uriPath = this.GetSchemaFilePath(CodeFilePath);
			if(uriPath != null)
			{
				XmlSchema schema = this.GetSchema(uriPath);
				if(schema != null)
				{
					ns = schema.TargetNamespace;
				}
			}
			return ns;
		}
		public string GetSchemaFilePath(string CodeFilePath)
		{
			string fileName = CodeFilePath.ToLower();
			foreach(string uriPath in _codeFilePaths.Keys)
			{
				string filePath = _codeFilePaths[uriPath] as string;
				filePath = filePath.ToLower();
				if(filePath.IndexOf(fileName) >= 0)
				{
					return uriPath;
				}
			}
			return null;
		}
		public string GetCodeFilePath(string UriPath)
		{
			return _codeFilePaths[UriPath] as string;
		}
		public void SetCodeFilePath(string UriPath)
		{
			string fileName = this.GetNamespaceName(UriPath,
				_addedSchemas.Keys);
			this.SetCodeFilePath(UriPath, fileName);
		}
		public void SetCodeFilePath(string UriPath, string FileNameNoExt)
		{
			string fileName = FileNameNoExt;
			string fileExt = "txt";
			switch(Schema.CodeGenerator.CodeLanguage)
			{
				case Schema.CodeGenerator.Language.CS:
					fileExt = "cs";
					break;
				case Schema.CodeGenerator.Language.VB:
					fileExt = "vb";
					break;
			}
			_codeFilePaths[UriPath] = 
				this.CodeFileFolder + fileName + "." + fileExt;
		}
		private Hashtable _addedSchemas = new Hashtable();
		public XmlSchema GetSchema(string UriPath)
		{
			return _addedSchemas[UriPath] as XmlSchema;
		}
		public void SetSchema(string UriPath, XmlSchema Schema)
		{
			_addedSchemas[UriPath] = Schema;
		}
		private XmlSchemas _schemas = new XmlSchemas();
		public XmlSchemas ReferenceSchemas
		{
			get { return _schemas; }
			set { _schemas = value; }
		}
		private XmlSchema _targetSchema = null;
		public XmlSchema TargetXmlSchema
		{
			get { return _targetSchema; }
		}
		private string _codeNamespaceName = null;
		public string CodeNamespaceName
		{
			get { return _codeNamespaceName; }
			set	{ _codeNamespaceName = this.GetValidNamespaceName(value); }
		}
		private string _currentUriPath = null;
		// CHANGE: 2006-06-26:	  Version 0.57 Beta
		// Made get access only and public.
		public string CurrentUriPath
		{
			get { return _currentUriPath; }
		}
		// CHANGE: 2006-06-26:	  Version 0.57 Beta
		// Added.
		public XmlSchema CurrentSchema
		{
			get
			{
				if ((this.CurrentUriPath != null) &&
					(_addedSchemas != null) &&
					(_addedSchemas.Count > 0))
				{
					return (XmlSchema)_addedSchemas[this.CurrentUriPath];
				}
				return null;
			}
		}
		public string CodeFilePath
		{
			get
			{
				if(_currentUriPath == null) return null;
				return this.GetCodeFilePath(_currentUriPath);
			}
		}
		public CodeNamespace CodeNamespace
		{
			get
			{
				if(_currentUriPath == null) return null;
				return this.GetCodeNamespace(_currentUriPath);
			}
		}
		private Hashtable _codeNamespaces = new Hashtable();
		public Hashtable CodeNamespaces
		{
			get { return _codeNamespaces; }
		}
		public CodeNamespace GetCodeNamespace(string UriPath)
		{
			return _codeNamespaces[UriPath] as CodeNamespace;
		}
		public void SetCodeNamespace(string UriPath, CodeNamespace 
			Namespace)
		{
			_codeNamespaces[UriPath] = Namespace;
		}
		static private CodeDomProvider _codeDomProvider = null;
		static public CodeDomProvider CodeDomProvider
		{
			get { return _codeDomProvider; }
			set { _codeDomProvider = value; }
		}
		static public object[] LoadInterfacesFromAssembly(Assembly Assembly,
			string TypeName)
		{
			ArrayList interfaces = new ArrayList();
			try
			{
				foreach(Type type in Assembly.GetTypes()) 
				{
					if( (type.IsClass == true) &&
						(type.IsAbstract == false) )
					{
						System.Type iType = type.GetInterface(TypeName);
						if(iType != null)
						{
							bool add = false;
							object implementor = Activator.CreateInstance(type);
							if(TypeName == _codeModifierInterface)
							{
								ICodeModifier modifier = implementor as ICodeModifier;
								if( (modifier != null) && modifier.Enabled )
								{
									add = true;
								}
							}
							if(add) interfaces.Add(implementor);
						}
					}
				}
			}
			catch(Exception e)
			{
				Diagnostics.Log.DefaultLog.LogException(e);
			}
			return (object[])interfaces.ToArray(typeof(object));
		}
		// CHANGE: 2006-06-26:	  Version 0.57 Beta
		// Find a concrete class, sub-class from abstract type.
		static public Type[] LoadTypesFromAssembly(Assembly assembly,
			Type abstractType)
		{
			ArrayList types = new ArrayList();
			try
			{
				foreach (Type type in assembly.GetTypes())
				{
					if ((type.IsClass == true) &&
						(type.IsAbstract == false) &&
						(type.IsSubclassOf(abstractType)))
					{
						types.Add(type);
					}
				}
			}
			catch (Exception e)
			{
				Diagnostics.Log.DefaultLog.LogException(e);
			}
			return (Type[])types.ToArray(typeof(Type));
		}
		private ICodeModifier[] LoadCodeModifiers(Assembly Asm)
		{
			ICodeModifier[] codeModifiers = null;
			try
			{
				object[] interfaces = CodeGenerator.LoadInterfacesFromAssembly(Asm, 
					_codeModifierInterface);
				Array typeArray = 
					Array.CreateInstance(typeof(ICodeModifier), 
					interfaces.Length);
				interfaces.CopyTo(typeArray, 0);
				codeModifiers = typeArray as ICodeModifier[];
			}
			catch (Exception e)
			{
				Diagnostics.Log.DefaultLog.LogException(e);
			}
			return codeModifiers;
		}
		private ICodeModifier[] LoadCodeModifiers(string CodeModiferAssemblyPath)
		{
			ICodeModifier[] codeModifiers = null;
			Assembly asm = null;
			try
			{
				asm = Assembly.LoadFrom(CodeModiferAssemblyPath);
			}
			catch (Exception e)
			{
				Diagnostics.Log.DefaultLog.LogException(e);
			}
			if (asm != null)
			{
				codeModifiers = this.LoadCodeModifiers(asm);
			}
			if( (codeModifiers != null) && (codeModifiers.Length == 0) )
			{
				Diagnostics.Log.DefaultLog.LogMessageLine(
					"File:<" + CodeModiferAssemblyPath + 
					"> does not contain usable ICodeModifier implementers.");
			}
			return codeModifiers;
		}
		private ICodeModifier[] LoadCodeModifiers(
			string[] CodeModifierAssemblyPaths)
		{
			ArrayList codeModifierArray = new ArrayList();
			ICodeModifier[] codeModifiers = this.LoadCodeModifiers(
				Assembly.GetExecutingAssembly());
			codeModifierArray.AddRange(codeModifiers);
			if(CodeModifierAssemblyPaths != null)
			{
				foreach(string asmPath in CodeModifierAssemblyPaths)
				{
					codeModifiers = this.LoadCodeModifiers(asmPath);
					if (codeModifiers != null)
					{
						codeModifierArray.AddRange(codeModifiers);
					}
				}
			}
			return (ICodeModifier[])codeModifierArray.ToArray(
				typeof(ICodeModifier));
		}
//#if NET_2_0
		SchemaImporterExtension[] GetExtenderTypes(ICodeModifier[] modifiers)
		{
			ArrayList extenders = new ArrayList();
			if (modifiers != null)
			{
				foreach (ICodeModifier modifier in this.CodeModifiers)
				{
					object extender = modifier.SchemaImporterExtension;
					if ((extender != null) &&
						extender.GetType().IsSubclassOf(
							typeof(SchemaImporterExtension)))
					{
						extenders.Add(extender);
					}
				}
			}
			return (SchemaImporterExtension[])
				extenders.ToArray(typeof(SchemaImporterExtension));
		}
//#endif // NET_2_0
		private XmlSchema ReadSchema(string SchemaFilePath)
		{
			XmlSchema schema = null;
			StreamReader sr = null;
			try
			{
				Uri uri = new Uri(SchemaFilePath);
				if( ! uri.IsFile)
				{
					HttpWebRequest w = 
						(HttpWebRequest)WebRequest.Create(uri.AbsoluteUri);
					HttpWebResponse r = (HttpWebResponse)w.GetResponse();
					sr = new StreamReader(r.GetResponseStream());
				}
				else
				{
					sr = new StreamReader(uri.LocalPath);
				}
				schema = XmlSchema.Read(sr, null);
				schema.SourceUri = SchemaFilePath;
			}
			catch(Exception e)
			{
				Diagnostics.Log.DefaultLog.LogException(e);
				throw;
			}
			finally
			{
				if(sr != null) sr.Close();
			}
			return schema;
		}
		private XmlSchemas ImportSchemas(string[] ImportSchemaFilePaths,
			XmlSchemas Schemas, Hashtable AddedSchemas)
		{
			for(int i = 0; i < ImportSchemaFilePaths.Length; i++)
			{
				XmlSchema schema = this.ReadSchema(ImportSchemaFilePaths[i]);
				Uri sourceUri = new Uri(schema.SourceUri);
				this.IncludeSchemas(_targetSchema, sourceUri, Schemas,
					AddedSchemas);
			}
			return Schemas;
		}
		private XmlSchemas IncludeSchemas(XmlSchema Parent,
			Uri SourceUri, XmlSchemas Schemas, Hashtable AddedSchemas)
		{
			foreach(XmlSchemaExternal externalSchema in Parent.Includes)
			{
				try
				{
					Uri schemaUri = 
						new Uri(SourceUri, externalSchema.SchemaLocation);
					string uriPath = this.GetUriPath(schemaUri);
					XmlSchema schema = this.ReadSchema(uriPath);
					if(AddedSchemas[uriPath] == null)
					{
						if(Schemas[schema.TargetNamespace] != null)
						{
							XmlSchema compSchema = 
								Schemas[schema.TargetNamespace];
							foreach(XmlSchemaObject schemaObj in schema.Items)
							{
								try
								{
									compSchema.Items.Add(schemaObj);
								}
								catch (Exception e)
								{
									Diagnostics.Log.DefaultLog.LogException(e);
								}
							}
						}
						else
						{
							Schemas.Add(schema);
						}
						AddedSchemas[uriPath] = schema;
						this.IncludeSchemas(schema, schemaUri, Schemas, 
							AddedSchemas);
					}
				}
				catch (Exception e)
				{
					Diagnostics.Log.DefaultLog.LogException(e);
				}
			}
			return Schemas;
		}
		private string GetValidNamespaceName(string NamespaceName)
		{
			NamespaceName = NamespaceName.Trim();
			char[] c = NamespaceName.ToCharArray();
			for(int i = 0; i < c.Length; i++)
			{
				if( (! char.IsLetterOrDigit(c[i])) &&
					(c[i] != '_') && (c[i] != '-') && 
					(c[i] != '.') )
				{
					c[i] = '*';
				}
			}
			for(int i = 0; i < c.Length; i++)
			{
				if(char.IsDigit(c[i]))
				{
					c[i] = '*';
				}
				else break;
			}
			string name = new string(c);
			name = name.Replace("*", "");
			name = name.Replace("-", "_");
			string[] nsParts = name.Split(new char[]{'.'});
			name = "";
			foreach(string nsPart in nsParts)
			{
				if(nsPart.Length > 0)
				{
					name += "." + char.ToUpper(nsPart[0]) + 
						nsPart.Substring(1);
				}
			}
			name = name.Trim(new char[]{'.'});
			if(name == "") name = "Unnamed";
			return name;
		}
		private string GetNamespaceName(string UriPath, ICollection UriPaths)
		{
			Uri uri = new Uri(UriPath);
			FileInfo fi = new FileInfo(uri.AbsolutePath);
			string nsName = this.GetFileName(UriPath, UriPaths);
			nsName = nsName.Replace(fi.Extension, "");
			return this.GetValidNamespaceName(nsName);
		}
		private string GetFileName(string UriPath, ICollection UriPaths)
		{
			Uri uriFileName = new Uri(UriPath);
			FileInfo fiFileName = new FileInfo(uriFileName.AbsolutePath);
			string fileName = fiFileName.Name;
			int fileCount = 0;
			foreach(string path in UriPaths)
			{
				if(path.ToLower().EndsWith(fileName.ToLower()))
				{
					++fileCount;
				}
			}
			if(fileCount > 1)
			{
				bool created = false;
				foreach(string path in UriPaths)
				{
					Uri uri = new Uri(path);
					if(uriFileName.AbsoluteUri == uri.AbsoluteUri) continue;
					FileInfo fi = new FileInfo(uri.AbsolutePath);
					if(fileName.ToLower() == fi.Name.ToLower())
					{
						DirectoryInfo diFileName = fiFileName.Directory;
						DirectoryInfo di = fi.Directory;
						string dirLevel = "";
						while(true)
						{
							string subFileName = fiFileName.FullName.Replace(
								diFileName.FullName, "");
							string sub = fi.FullName.Replace(di.FullName, "");
							if(subFileName.ToLower() == sub.ToLower())
							{
								dirLevel = diFileName.FullName.Replace(
									diFileName.Parent.FullName, "");
								diFileName = diFileName.Parent;
								di = di.Parent;
							}
							else
							{
								fileName = dirLevel + "_" +
									fileName;
								created = true;
								break;
							}
						}
					}
					if(created) break;
				}
			}
			fileName = fileName.Trim(new char[]{'.','/','\\'});
			fileName =
				char.ToUpper(fileName[0]) + fileName.Substring(1);
			return fileName;
		}
		private void ExportCode(XmlSchema Schema, 
			CodeNamespace CodeNamespace, XmlSchemaImporter SchemaImporter)
		{
//#if NET_2_0
			CodeGenerationOptions xmlCodeOptions = CodeGenerationOptions.None;
			XmlCodeExporter codeExporter = new XmlCodeExporter(CodeNamespace,
				new CodeCompileUnit(), xmlCodeOptions);
//#else // ! NET_2_0
//            XmlCodeExporter codeExporter = new XmlCodeExporter(CodeNamespace);
//#endif // NET_2_0
			// Iterate schema items (top-level elements only) and generate code 
			// for each item.
			// CHANGE: 2006-06-19:	  Version 0.56 Beta
			// Added XmlSchemaElement's to ArrayList. Enumerated array list and
			// import/export mappings to avoid 'collection changed during iteration'
			// exception.
			ArrayList elements = new ArrayList();
			foreach (XmlSchemaElement element in Schema.Elements.Values)
			{
				elements.Add(element);
			}
			foreach (XmlSchemaElement element in elements)
			{
				// Import the mapping first
				try
				{
					XmlTypeMapping map =
						SchemaImporter.ImportTypeMapping(
							element.QualifiedName);
					// Export the code finally
					codeExporter.ExportTypeMapping(map);
				}
				catch(Exception e)
				{
					Diagnostics.Log.DefaultLog.LogException(e);
				}
			}
		}
		public string GetUriPath(Uri SchemaUri)
		{
			string uriPath = SchemaUri.AbsoluteUri;
			if(SchemaUri.IsFile) uriPath = SchemaUri.LocalPath;
			return uriPath;
		}
		public string GetUriPath(XmlSchema Schema)
		{
			Uri uri = new Uri(Schema.SourceUri);
			return this.GetUriPath(uri);
		}
		public bool IsTargetSchema(string UriPath)
		{
			return (UriPath == this.GetUriPath(this.TargetXmlSchema));
		}
		public CodeNamespace CreateNamespaceFromTarget(bool CopyNone)
		{
			XmlSchema schema = this.TargetXmlSchema;
			string uriPath = this.GetUriPath(schema);
			CodeNamespace ns = this.GetCodeNamespace(uriPath);
			return this.CreateNamespace(ns, CopyNone);
		}
		public CodeNamespace CreateNamespace(CodeNamespace Namespace,
			bool CopyNone)
		{
			// Create with same name
			CodeNamespace codeNs = new CodeNamespace(Namespace.Name);
			if(CopyNone) return codeNs;
			// Copy the Coments
			foreach(CodeCommentStatement comment in Namespace.Comments)
			{
				codeNs.Comments.Add(comment);
			}
			// Copy the Imports
			foreach(CodeNamespaceImport import in Namespace.Imports)
			{
				codeNs.Imports.Add(import);
			}
			// Copy the collection of Types
			CodeTypeDeclaration[] types = new CodeTypeDeclaration[0];
			Namespace.Types.CopyTo(types, 0);
			codeNs.Types.AddRange(types);
			// Copy the UserData
			foreach(object key in Namespace.UserData.Keys)
			{
				object data = Namespace.UserData[key];
				codeNs.UserData.Add(key, data);
			}
			return codeNs;
		}
		private bool IsSchemaMapType(System.Type Type)
		{
			System.Type[] mapTypes = new System.Type[]
			{
				typeof(XmlSchemaType),
				typeof(XmlSchemaElement),
			};
			foreach(System.Type t in mapTypes)
			{
				if( (Type == t) || (Type.IsSubclassOf(t)) ) return true;
			}
			return false;
		}
		private string GetMappedName(XmlSchemaObject SchemaObj)
		{
			System.Type objType = SchemaObj.GetType();
			if(this.IsSchemaMapType(objType))
			{
				PropertyInfo pi = objType.GetProperty("Name");
				if(pi != null)
				{
					return pi.GetValue(SchemaObj, null).ToString();
				}
			}
			return null;
		}
		private CodeNamespace CreateCommonCodeNamespace()
		{
			CodeNamespace nsCommon = 
				this.CreateNamespaceFromTarget(true);
			Hashtable commonTypes = new Hashtable();
			foreach(string u1 in _codeFilePaths.Keys)
			{
				CodeNamespace ns1 = _codeNamespaces[u1] as CodeNamespace;
				foreach(string u2 in _codeFilePaths.Keys)
				{
					CodeNamespace ns2 = _codeNamespaces[u2] as CodeNamespace;
					if(ns1 == ns2) continue;
					foreach(CodeTypeDeclaration t1 in ns1.Types)
					{
						foreach(CodeTypeDeclaration t2 in ns2.Types)
						{
							if(t1.Name == t2.Name)
							{
								commonTypes[t1.Name] = t1;
								break;
							}
						}
					}
				}
			}
			if(commonTypes.Count > 0)
			{
				foreach(string uriPath in _codeFilePaths.Keys)
				{
					ArrayList remove = new ArrayList();
					CodeNamespace ns = 
						_codeNamespaces[uriPath] as CodeNamespace;
					foreach(CodeTypeDeclaration type in ns.Types)
					{
						if(commonTypes[type.Name] != null)
						{
							remove.Add(type);
						}
					}
					if(remove.Count > 0)
					{
						foreach(CodeTypeDeclaration type in remove)
						{
							ns.Types.Remove(type);
						}
					}
				}
				foreach(string name in commonTypes.Keys)
				{
					nsCommon.Types.Add(commonTypes[name] as 
						CodeTypeDeclaration);
				}
			}
			return nsCommon;
		}
		// CHANGE: 2006-06-26:	  Version 0.57 Beta
		// Initialize the generator.
		private void Initialize()
		{
			foreach (ICodeModifier modifier in this.CodeModifiers)
			{
				modifier.Initialize(this);
			}
		}
		// CHANGE: 2006-06-26:	  Version 0.57 Beta
		// Deinitialize the generator.
		private void Deinitialize()
		{
			foreach (ICodeModifier modifier in this.CodeModifiers)
			{
				modifier.Deinitialize(this);
			}
		}
		private CodeNamespace ExtractNamespace(CodeNamespace Namespace,
			string UriPath)
		{
			// Flag to indicate common Namespace name.
			bool commonNamespace = true;
			this._currentUriPath = UriPath;
			XmlSchema schema = this.GetSchema(UriPath);
			string nsName = Namespace.Name;
			if (!commonNamespace)
			{
				if (schema.SourceUri != this.TargetXmlSchema.SourceUri)
				{
					nsName += "." + this.GetNamespaceName(UriPath,
						_addedSchemas.Keys);
				}
			}
			// Create an empty namespace from the master
			CodeNamespace codeNs = this.CreateNamespace(Namespace, true);
			codeNs.Name = nsName;
			// Remove what cannot be mapped to the schema
			// Iterate schema items and map each item to the namespace class.
			foreach (XmlSchemaObject item in schema.Items)
			{
				string itemName = this.GetMappedName(item);
				if (itemName != null)
				{
					bool mapped = false;
					foreach (CodeTypeDeclaration type in Namespace.Types)
					{
						if (type.Name == itemName)
						{
							if (codeNs.Types.Contains(type))
							{
								// CHANGE: 2006-06-19:	  Version 0.56 Beta
								// Updated message, and add as generator warning.
								string w = 
									"Namespace already contains type '" + type.Name + "'. " +
									"(Check for XSD types and elements with the same name)";
								this.AddWarning(w);
								// CHANGE: 2006-06-19:	  Version 0.56 Beta
								// Throw and catch the warning as an exception and log 
								// error without terminating generation.
								try
								{
									throw new InvalidOperationException(w);
								}
								catch (Exception e)
								{
									Diagnostics.Log.DefaultLog.LogException(e);
								}
							}
							else
							{
								codeNs.Types.Add(type);
							}
							mapped = true;
							break;
						}
						CodeAttributeDeclarationCollection attributes =
							CodeModifier.CodeSerializationAttribute.
								GetAttribute(type, typeof(XmlTypeAttribute));
						foreach (CodeAttributeDeclaration typeAttr in attributes)
						{
							CodeAttributeArgument typeArg =
								CodeModifier.CodeSerializationAttribute.
								GetArgument(typeAttr, "TypeName");
							if (typeArg != null)
							{
								if (typeArg.Value is CodePrimitiveExpression)
								{
									string argVal =
										((CodePrimitiveExpression)(typeArg.Value)).
										Value.ToString();
									if (argVal == itemName)
									{
										codeNs.Types.Add(type);
										mapped = true;
										break;
									}
								}
							}
						}
					}
					if (!mapped)
					{
						string w = "Schema object '" + itemName +
							"', type=[" + item.GetType().ToString() +
							"] not mapped to code.";
						this.AddWarning(w);
						// CHANGE: 2006-06-19:	  Version 0.56 Beta
						// Throw and catch the warning as an exception and log 
						// error without terminating generation.
						// Uncomment the code below to add to log.
						//try
						//{
						//    throw new InvalidOperationException(w);
						//}
						//catch (Exception e)
						//{
						//    Diagnostics.Log.DefaultLog.LogException(e);
						//}
					}
				}
			}
			foreach (CodeTypeDeclaration type in codeNs.Types)
			{
				try
				{
					Namespace.Types.Remove(type);
				}
				catch (Exception e)
				{
					Diagnostics.Log.DefaultLog.LogException(e);
				}
			}
			this._currentUriPath = null;
			return codeNs;
		}
		public int Generate()
		{
			// Load the code modifiers.
			if(this.CodeModifiers == null)
			{
				this.CodeModifiers = 
					this.LoadCodeModifiers(
						this.CodeModifierAssemblyPaths);
			}
			// CHANGE: 2006-06-26:	  Version 0.57 Beta
			// Extend the importer to *really* customize code-generation!
//#if NET_2_0
			if (this.SchemaImporterExtenders == null)
			{
				this.SchemaImporterExtenders =
					GetExtenderTypes(this.CodeModifiers);
			}
//#endif // NET_2_0
			// CHANGE: 2006-06-26:	  Version 0.57 Beta
			// Initialize the generator.
			this.Initialize();
			Diagnostics.Log.DefaultLog.LogMessage(
				this.GetLogDetail(LogDetail.Modifiers));
			int numberOfFiles = 
				this.Generate(this.CodeFileFolder, this.ReferenceSchemas,
					this.CodeModifiers);
			// CHANGE: 2006-06-26:	  Version 0.57 Beta
			// Deinitialize the generator.
			this.Deinitialize();
			return numberOfFiles;
		}
		public string GenerateCode(
			string UriPath,
			string FileNameNoExt,
			CodeNamespace Namespace,
			ICodeModifier[] CodeModifiers)
		{
			string lastUriPath = this.CurrentUriPath;
			this.SetCodeNamespace(UriPath, Namespace);
			this.SetCodeFilePath(UriPath, FileNameNoExt);
			this._currentUriPath = UriPath;
			// Apply the code modifiers.
			if( (CodeModifiers != null) &&
				(CodeModifiers.Length > 0) )
			{
				foreach(ICodeModifier codeModifier in CodeModifiers)
				{
					if( ! codeModifier.RunOnce)
					codeModifier.Execute(this);
				}
			}
			string code = "";
			// Write the file.
			StringBuilder sb = new StringBuilder(64*1024);
			using(StringWriter sw = new StringWriter(sb))
			{
//#if	NET_2_0
				// Generate the code
				CodeGenerator.CodeDomProvider.GenerateCodeFromNamespace(
					Namespace, sw, CodeGenerator.CodeGeneratorOptions);
//#else // ! NET_2_0
                //// Create the code generator
                //ICodeGenerator generator = 
                //    CodeGenerator.CodeDomProvider.CreateGenerator();
                //generator.GenerateCodeFromNamespace(
                //    Namespace, sw, CodeGenerator.CodeGeneratorOptions);
//#endif // NET_2_0
				sw.Close();
				code = sb.ToString();
			}
			this.SetSourceCode(UriPath, code);
			this._currentUriPath = lastUriPath;
			return code;
		}
		public static void XmlSchemaValidationHandler(object sender, ValidationEventArgs args)
		{
			if (args.Severity != XmlSeverityType.Error)
			{
				Diagnostics.Log.DefaultLog.LogException(args.Exception);
			}
			else
			{
				throw args.Exception;
			}
		}
		// CHANGE: 2006-06-26:	  Version 0.57 Beta
		// Allow access only via the Generate() method.
		private int Generate(string CodeFileFolder,
			XmlSchemas Schemas, 
			ICodeModifier[] CodeModifiers)
		{
			// Compile the target schema, to ensure 
			// that all types can be resolved during
			// schema element type mappings...
			try
			{
				this.TargetXmlSchema.Compile(
					new ValidationEventHandler(CodeGenerator.XmlSchemaValidationHandler));
			}
			catch (Exception e)
			{
				Diagnostics.Log.DefaultLog.LogException(e);
			}
			// Create the schema importer object.
			XmlSchemaImporter schemaImporter = 
				new XmlSchemaImporter(this.ReferenceSchemas);
			// CHANGE: 2006-06-26:	  Version 0.57 Beta
			// Extend the importer to *really* customize code-generation!
//#if NET_2_0
			foreach (SchemaImporterExtension extender in this.SchemaImporterExtenders)
			{
				schemaImporter.Extensions.Add(extender);
			}
//#else // ! NET_2_0
//            // TODO: How do we extend the XmlSchemaImporter for .NET 1.1?
//#endif // NET_2_0
			// CodeDOM namespace for the XmlCodeExporter to put 
			// classes in.
			// CHANGE: 26-06-29:	 Version 0.57 Beta
			// Change initialization of codeDOMs.
			// Create the master namespace
			_globalCodeNamespace = new CodeNamespace(this.CodeNamespaceName);
			// Create a copy that will be worked with.
			CodeNamespace masterNs =
				new CodeNamespace(this.CodeNamespaceName);
			try
			{
				// CHANGE: 26-06-29:	 Version 0.57 Beta
				// TODO: why must ExportCode be called twice to get all the
				// types?
				this.ExportCode(this.TargetXmlSchema, _globalCodeNamespace, 
					schemaImporter);
				_globalCodeNamespace = new CodeNamespace(this.CodeNamespaceName); 
				this.ExportCode(this.TargetXmlSchema, _globalCodeNamespace,
					schemaImporter);
				this.ExportCode(this.TargetXmlSchema, masterNs,
					schemaImporter);
			}
			catch(Exception e)
			{
				Diagnostics.Log.DefaultLog.LogException(e);
			}
			// Divide up the code into the file-base generation scheme
			CodeNamespace codeNs = null;
			foreach(string uriPath in _addedSchemas.Keys)
			{
				if(this.IsTargetSchema(uriPath)) continue;
				codeNs = this.ExtractNamespace(masterNs, uriPath);
				this.SetCodeNamespace(uriPath, codeNs);
			}
			codeNs = 
				this.ExtractNamespace(masterNs, 
					this.GetUriPath(this.TargetXmlSchema));
			this.SetCodeNamespace(
				this.GetUriPath(this.TargetXmlSchema), codeNs);
			if(masterNs.Types.Count > 0)
			{
				foreach(CodeTypeDeclaration type in masterNs.Types)
				{
					codeNs.Types.Add(type);
				}
			}
			foreach(string uriPath in _addedSchemas.Keys)
			{
				this.SetCodeFilePath(uriPath);
			}
			// Run the code generator checker..
			CodeGenerateCheck.Execute(this);
			foreach(string uriPath in _codeFilePaths.Keys)
			{
				this._currentUriPath = uriPath;
				// Apply the code modifiers.
				if( (this.CodeModifiers != null) &&
					(this.CodeModifiers.Length > 0) )
				{
					foreach(ICodeModifier codeModifier in this.CodeModifiers)
					{
						if( ! codeModifier.RunOnce)
						{
							codeModifier.Execute(this);
						}
					}
				}
				this._currentUriPath = null;
			}
			// Create a common class namespace
			_commonCodeNamespace = this.CreateCommonCodeNamespace();
			// Create the source code
			foreach(string uriPath in _codeFilePaths.Keys)
			{
				this._currentUriPath = uriPath;
				string code = "";
				// Write the file.
				StringBuilder sb = new StringBuilder(64*1024);
				using(StringWriter sw = new StringWriter(sb))
				{
//#if	NET_2_0
					// Generate the code
					CodeGenerator.CodeDomProvider.GenerateCodeFromNamespace(
						this.CodeNamespace, sw, 
						CodeGenerator.CodeGeneratorOptions);
//#else // ! NET_2_0
//                    // Create the code generator
//                    ICodeGenerator generator = 
//                        CodeGenerator.CodeDomProvider.CreateGenerator();
//                    generator.GenerateCodeFromNamespace(
//                        this.CodeNamespace, sw, 
//                        CodeGenerator.CodeGeneratorOptions);
//#endif // NET_2_0
					sw.Close();
					code = sb.ToString();
				}
				this.SetSourceCode(uriPath, code);
				this._currentUriPath = null;
			}
			// Run the 'run-once' code modifiers
			this._currentUriPath = this.GetUriPath(this.TargetXmlSchema);
			if( (this.CodeModifiers != null) &&
				(this.CodeModifiers.Length > 0) )
			{
				foreach(ICodeModifier codeModifier in this.CodeModifiers)
				{
					if(codeModifier.RunOnce)
					{
						codeModifier.Execute(this);
					}
				}
			}
			this._currentUriPath = null;
			// Write the files
			if( (CodeFileFolder != null) && (CodeFileFolder != "") )
			{
				Diagnostics.Log.DefaultLog.LogMessage(
					this.GetLogDetail(LogDetail.Files));
				foreach(string uriPath in _codeFilePaths.Keys)
				{
					this._currentUriPath = uriPath;
					string code = this.GetSourceCode(uriPath);
					try
					{
						FileInfo fileInfo = new FileInfo(this.GetCodeFilePath(uriPath));
						if( ! fileInfo.Directory.Exists)
						{
							fileInfo.Directory.Create();
						}
						using(StreamWriter sw = new StreamWriter(fileInfo.FullName, false))
						{
							sw.Write(code);
							sw.Close();
						}
					}
					catch(Exception e)
					{
						Diagnostics.Log.DefaultLog.LogException(e);
						throw;
					}
					this._currentUriPath = null;
				}
			}
			return this.NumberOfCodeFiles;
		}
		public enum Language { Unknown, CS, VB };
		static public Language CodeLanguage
		{
			get
			{
				if(CodeGenerator.CodeDomProvider is CSharpCodeProvider) 
					return Language.CS;
				if(CodeGenerator.CodeDomProvider is VBCodeProvider)
					return Language.VB;
				return Language.Unknown;
			}
		}
		static private bool _defaultInstantiateObjects = true;
		static public bool DefaultInstantiateObjects
		{
			get { return _defaultInstantiateObjects; }
			set { _defaultInstantiateObjects = value; }
		}
		public enum LogDetail
		{
			Startup,
			Schema,
			Code,
			Modifiers,
			Files
		}
		private string[] GetLogDetail(LogDetail DetailType)
		{
			string[] content = null;
			switch(DetailType)
			{
				case LogDetail.Startup:
				{
					content = new string[]
					{
						"CodeXS started.",
						"Version           : " + this.Version,
						"Runtime version   : " + Assembly.GetExecutingAssembly().ImageRuntimeVersion,
						".NET Version      : " + System.Environment.Version.ToString(),
						Diagnostics.Log.LineTerminator,
					};
					break;
				}
				case LogDetail.Schema:
				{
					ArrayList contentList = new ArrayList();
					contentList.Add("Schemas:");
					contentList.Add("Target Schema:");
					contentList.Add(this.TargetXmlSchema.SourceUri);
					contentList.Add("Reference Schemas:");
					foreach(string uriPath in this._addedSchemas.Keys)
					{
						contentList.Add(uriPath);
					}
					contentList.Add(Diagnostics.Log.LineTerminator);
					content = (string[])contentList.ToArray(typeof(string));
					break;
				}
				case LogDetail.Code:
				{
					ArrayList contentList = new ArrayList();
					contentList.Add("Code Generation:");
					string codeLanguage = "Unknown";			
					if(CodeGenerator.CodeDomProvider is CSharpCodeProvider)
					{
						codeLanguage = "C#.NET";
					}
					if(CodeGenerator.CodeDomProvider is VBCodeProvider)
					{
						codeLanguage = "VB.NET";
					}
					contentList.Add("Code Language     : " + codeLanguage);
					contentList.Add("Code Namespace    : " + this.CodeNamespaceName);
					contentList.Add(Diagnostics.Log.LineTerminator);
					content = (string[])contentList.ToArray(typeof(string));
					break;
				}
				case LogDetail.Modifiers:
				{
					ArrayList contentList = new ArrayList();
					contentList.Add("Code Modifiers:");
					if(this.CodeModifiers != null)
					{
						foreach(ICodeModifier modifier in this.CodeModifiers)
						{
							contentList.Add(modifier.Name);
						}
					}
					else
					{
						contentList.Add("None specified");
					}
					contentList.Add(Diagnostics.Log.LineTerminator);
					content = (string[])contentList.ToArray(typeof(string));
					break;
				}
				case LogDetail.Files:
				{
					ArrayList contentList = new ArrayList();
					contentList.Add("Code Files:");
					foreach(string path in this.CodeFilePaths)
					{
						FileInfo fileInfo = new FileInfo(path);
						contentList.Add(fileInfo.Name);
					}
					contentList.Add(Diagnostics.Log.LineTerminator);
					content = (string[])contentList.ToArray(typeof(string));
					break;
				}
			}
			return content;
		}
		// CHANGE: 2006-06-26:	  Version 0.57 Beta
		// Allow logging from add-ins into the CodeGenerator log.
		public CodeXS.Diagnostics.Log Log
		{
			get 
			{ 
				return CodeXS.Diagnostics.Log.DefaultLog; 
			}
		}
		// CHANGE: 2006-06-29:	 Version 0.57 Beta
		// Removed ISchemaObjectReader objects.
		public CodeGenerator(
			string CodeFileFolder,
			string CodeNamespace,
			CodeDomProvider CodeDomProvider,
			string TargetSchemaFilePath, 
			string[] ImportSchemaFilePaths, 
			CodeGeneratorOptions CodeGeneratorOptions,
			string[] CodeModifierAssemblyPaths,
			bool GenerateNow)
		{
			this.CodeFileFolder = CodeFileFolder;
			// Save the code file path
			if ( ! this.CodeFileFolder.EndsWith("\\"))
			{
				this.CodeFileFolder += "\\";
			}
			// If the log is null, create a new log
			if(Diagnostics.Log.DefaultLog == null)
			{
				Diagnostics.Log.DefaultLog = 
					new Diagnostics.Log(Diagnostics.Log.LogDestination.LogFile);
				string logFilePath = Diagnostics.Log.DefaultLog.LogFilePath;
				Diagnostics.Log.DefaultLog.LogFilePath =
					this.CodeFileFolder + logFilePath;
			}
			Diagnostics.Log.DefaultLog.LogMessage(
				GetLogDetail(LogDetail.Startup));
			// Try make a uri - if it fails get the full file path.
			try
			{
				Uri uri = new Uri(TargetSchemaFilePath);
			}
			catch(Exception )
			{
				FileInfo fi = new FileInfo(TargetSchemaFilePath);
				TargetSchemaFilePath = fi.FullName;
			}
			this.CodeNamespaceName = CodeNamespace;
			this.CodeModifierAssemblyPaths =
				CodeModifierAssemblyPaths;
			if(CodeGenerator.CodeGeneratorOptions == null)
			{
				CodeGenerator.CodeGeneratorOptions = new CodeGeneratorOptions();
				CodeGenerator.CodeGeneratorOptions.BracingStyle = "C";
				CodeGenerator.CodeGeneratorOptions.BlankLinesBetweenMembers = false;
				CodeGenerator.CodeGeneratorOptions.IndentString = "\t";
			}
//#if NET_2_0
			CodeGenerator.CodeGeneratorOptions.VerbatimOrder = true;
//#endif // NET_2_0
			CodeGenerator.CodeDomProvider = CodeDomProvider;
			if(CodeGenerator.CodeDomProvider == null)
			{
				CodeGenerator.CodeDomProvider = new CSharpCodeProvider();
			}
			// Read the target schema.
			_targetSchema = ReadSchema(TargetSchemaFilePath);
			// Add the target schema
			_schemas.Add(_targetSchema);
			_addedSchemas[this.GetUriPath(_targetSchema)] = _targetSchema;
			// Add the explicitly referenced schemas.
			if(ImportSchemaFilePaths != null)
			{
				this.ImportSchemas(ImportSchemaFilePaths, _schemas,
					_addedSchemas);
			}
			// Add the implicitly referenced schemas.
			this.IncludeSchemas(_targetSchema,
				new Uri(TargetSchemaFilePath), _schemas, _addedSchemas);
			Diagnostics.Log.DefaultLog.LogMessage(
				GetLogDetail(LogDetail.Schema));
			Diagnostics.Log.DefaultLog.LogMessage(
				GetLogDetail(LogDetail.Code));
			if(GenerateNow)
			{
				this.Generate();
			}
		}
		private static bool _dateTypeDefined = false;
		public static bool DateTypeDefined
		{
			get { return _dateTypeDefined; }
			set { _dateTypeDefined = value; }
		}
		private static bool _stringTypeDefined = false;
		public static bool StringTypeDefined
		{
			get { return _stringTypeDefined; }
			set { _stringTypeDefined = value; }
		}
		// CHANGE: 2006-06-19:	  Version 0.56 Beta
		// Query availability of specific modifiers based on
		// ICodeModifier.Name value.
		public bool IsCodeModifierEnabled(string name)
		{
			foreach (ICodeModifier modifier in this.CodeModifiers)
			{
				if (modifier.Name == name)
				{
					return modifier.Enabled;
				}
			}
			return false;
		}
	}
}
